// 参考： https://threejsfundamentals.org/threejs/lessons/threejs-post-processing.html
import { 
  AmbientLight,
  Color,
  Mesh,
  MeshPhongMaterial,
  PerspectiveCamera,
  Scene, 
  SphereGeometry, 
  SpotLight, 
  TextureLoader, 
  Vector2, 
  WebGLRenderer
} from "three";
import stats from '../utils/stats';
// 调用
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass';
import { BloomPass } from 'three/examples/jsm/postprocessing/BloomPass';
import { DotScreenPass } from 'three/examples/jsm/postprocessing/DotScreenPass';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass';
import { CopyShader } from 'three/examples/jsm/shaders/CopyShader'
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { GlitchPass } from 'three/examples/jsm/postprocessing/GlitchPass';
import { HalftonePass } from 'three/examples/jsm/postprocessing/HalftonePass';
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass';
import { GUI } from "dat.gui";

let scene = new Scene();
let renderer = setRenderer();
let camera = setCamera();

setLight();

let { earth } = addEarth();
scene.add(earth);

// 设置效果
let renderPass = new RenderPass(scene, camera);
let effectCopy = new ShaderPass(CopyShader);
effectCopy.renderToScreen = true;

// 故障效果
let composer = new EffectComposer(renderer);
let glitchPass = new GlitchPass()
composer.addPass(renderPass);
composer.addPass(glitchPass);
composer.addPass(effectCopy);

let filmComposer = new EffectComposer(renderer);
// 半色调效果
let halftonePass = new HalftonePass(window.innerWidth, window.innerHeight, {});
filmComposer.addPass(renderPass);
filmComposer.addPass(halftonePass);
filmComposer.addPass(effectCopy);

let outlinePass = new OutlinePass(
  new Vector2(window.innerWidth, window.innerHeight),
  scene,
  camera,
  [earth]
);
let bloomComposer = new EffectComposer(renderer);
bloomComposer.addPass(renderPass);
bloomComposer.addPass(outlinePass);
bloomComposer.addPass(effectCopy);

// 光辉效果
let unrealBloomPass = new UnrealBloomPass(
  new Vector2(2, 2),
  1.5,
  0.4,
  0.85
);
let dotScreenComposer = new EffectComposer(renderer);
dotScreenComposer.addPass(renderPass);
dotScreenComposer.addPass(unrealBloomPass);
dotScreenComposer.addPass(effectCopy);

let gui = addGUI(unrealBloomPass, halftonePass);
addHalftonePassControls(gui, {}, halftonePass, (htp: any) => {
  filmComposer = new EffectComposer(renderer);
  filmComposer.addPass(renderPass);
  filmComposer.addPass(htp);
  filmComposer.addPass(effectCopy);
})
addOutlinePassControls(gui, {}, outlinePass);

const trackballControls = new OrbitControls(camera, renderer.domElement);
const halfWidth = window.innerWidth / 2;
const halfHeight = window.innerHeight / 2;
render();

function render() {
  stats.update();
  trackballControls.update();

  earth.rotation.y += 0.001;

  // 防止处理后的效果被清除
  renderer.autoClear = false;
  renderer.clear();

  renderer.setViewport(0, halfHeight, halfWidth, halfHeight);
  composer.render();

  renderer.setViewport(halfWidth, halfHeight, halfWidth, halfHeight);
  bloomComposer.render();

  renderer.setViewport(0, 0, halfWidth, halfHeight);
  dotScreenComposer.render();

  renderer.setViewport(halfWidth, 0, halfWidth, halfHeight);
  filmComposer.render();

  requestAnimationFrame(render);
}


function setRenderer() {
  const renderer = new WebGLRenderer({ antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 支持阴影生成 
  renderer.shadowMap.enabled = true;
  document.body.appendChild(renderer.domElement);

  return renderer
}

function setCamera(): PerspectiveCamera {
  let camera = new PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
  camera.position.set(0, 20, 40);
  scene.add( camera );

  return camera;
}

// 灯光设置
function setLight() {
  scene.add(new AmbientLight(0x444444));
  scene.add(new AmbientLight(0x343434));
  
  let spotLight = new SpotLight(0xffffff);
  spotLight.position.set(-10, 30, 40);
  spotLight.shadow.mapSize.width = 2048;
  spotLight.shadow.mapSize.height = 2048;
  spotLight.shadow.camera.fov = 15;
  spotLight.decay =2;
  spotLight.penumbra = 0.05;
  spotLight.castShadow = true;

  scene.add(spotLight);
  // scene.add(new SpotLightHelper(spotLight));
}

function addGUI(unrealBloomPass: UnrealBloomPass, halftonePass: HalftonePass) {
  const gui = new GUI();
  let controls = {
    grayScale: false,
    noiseIntensity: 0.8,
    scanlinesIntensity: 0.325,
    scanlinesCount: 256,

    dtsize: 64,

    strength: 1.5,
    radius: 0.4,
    threshold: 0.85,
    resolution: 256,

    centerX: 0.5,
    centerY: 0.5,
    angle: 1,
    scale: 1
  }

  let glFolder = gui.addFolder('故障效果');
  glFolder.add(controls, 'dtsize', 2, 1024, 2).onChange(val => {
    composer.passes[1] = new GlitchPass(val);
  })

  let ubFolder = gui.addFolder('光辉效果');
  ubFolder.add(controls, 'resolution', 2, 1024, 2).name('resolution').onChange(val => {
    unrealBloomPass.resolution = new Vector2(val, val);
  });
  ubFolder.add(controls, 'radius', 0, 10, 0.01).name('半径').onChange(val => {
    unrealBloomPass.radius = val
  });
  ubFolder.add(controls, 'threshold', 0, 1, 0.01).name('threshold').onChange(val => {
    unrealBloomPass.threshold = val;
  });
  ubFolder.add(controls, 'strength', 0, 10, 0.1).name('strength').onChange(val => {
    unrealBloomPass.strength = val;
  });

 
  // let bloom = gui.addFolder('泛光效果');
  // bloom.add(controls, 'strength', 0, 5, 0.01).name('泛光强度').onChange(val => {
  //   // bloomPass.copyUniforms.opacity.value = val;
  //   // bloomComposer.passes[1] = new BloomPass(val)
  //   updateBloom();
  // });
  // bloom.add(controls, 'kernelSize', 10, 100, 1).name('偏移量').onChange(updateBloom);
  // bloom.add(controls, 'sigma', 1, 8, 0.01).name('税利程度').onChange(updateBloom);
  // bloom.add(controls, 'resolution', 100, 256, 10).name('解析图').onChange(updateBloom);

  // let dsFolder = gui.addFolder('点集效果');
  // dsFolder.add(controls, 'centerX', 0, 5, 0.01).onChange(val => {
  //   dotScreenPass.uniforms.center.value.copy(new Vector2(val, controls.centerY));
  // });
  // dsFolder.add(controls, 'centerY', 0, 5, 0.01).onChange(val => {
  //   dotScreenPass.uniforms.center.value.copy(new Vector2(controls.centerX, val));
  // });
  // dsFolder.add(controls, 'angle', 0, 3.14, 0.01).name('角度').onChange(val => {
  //   dotScreenPass.uniforms.angle.value = val;
  // });
  // dsFolder.add(controls, 'scale', 0, 10, 1).name('缩放').onChange(val => {
  //   dotScreenPass.uniforms.scale.value = val;
  // });

  return gui
}

function addEarth(): { earth: Mesh } {
  let loader = new TextureLoader();
  let material = new MeshPhongMaterial({
    map: loader.load('/textures/earth/Earth.png'),
    normalMap: loader.load('/textures/earth/EarthNormal.png'),
    specularMap: loader.load('/textures/earth/EarthSpec.png'),
    specular: new Color(0x4444aa),
    normalScale: new Vector2(6, 6),
    shininess: 0.5
  });
  let earth = new Mesh(
    new SphereGeometry(13, 40, 40),
    material
  );
  
  return {
    earth
  }
}

function addHalftonePassControls(gui: GUI, controls: any, htshader: HalftonePass, callback: Function) {

  controls.shape = 1;
  controls.radius = 4;
  controls.rotateR = Math.PI / 12 * 1;
  controls.rotateG = Math.PI / 12 * 2;
  controls.rotateB = Math.PI / 12 * 2;
  controls.scatter = 0;
  controls.width = 1;
  controls.height = 1;
  controls.blending = 1;
  controls.blendingMode = 1;
  controls.greyscale = false;

  function applyParams() {
    let newPass = new HalftonePass(controls.width, controls.height, controls);
    callback(newPass);
  }

  let htFolder = gui.addFolder('半色调效果');
  htFolder.add(controls, "shape", {dot: 1, ellipse: 2, line: 3, square: 4}).onChange(applyParams);
  htFolder.add(controls, "radius", 0, 40, 0.1).onChange(applyParams);
  htFolder.add(controls, "rotateR", 0, Math.PI*2, 0.1).onChange(applyParams);
  htFolder.add(controls, "rotateG", 0, Math.PI*2, 0.1).onChange(applyParams);
  htFolder.add(controls, "rotateB", 0, Math.PI*2, 0.1).onChange(applyParams);
  htFolder.add(controls, "scatter", 0, 2, 0.1).onChange(applyParams);
  htFolder.add(controls, "width", 0, 15, 0.1).onChange(applyParams);
  htFolder.add(controls, "height", 0, 15, 0.1).onChange(applyParams);
  htFolder.add(controls, "blending", 0, 2, 0.01).onChange(applyParams);
  htFolder.add(controls, "blendingMode", {linear: 1, multiply: 2, add: 3, lighter: 4, darker: 5}).onChange(applyParams);
  htFolder.add(controls, "greyscale").onChange(applyParams);
}

function addOutlinePassControls(gui: GUI, controls: any, outlinePass: OutlinePass) {

  controls.edgeStrength = 3.0,
  controls.edgeGlow = 0.0,
  controls.edgeThickness = 1.0,
  controls.pulsePeriod = 0,
  controls.usePatternTexture = false

  var folder = gui.addFolder("OutlinePass");
  folder.add( controls, 'edgeStrength', 0.01, 10 ).onChange( function ( value ) { outlinePass.edgeStrength = Number( value );});
  folder.add( controls, 'edgeGlow', 0.0, 1 ).onChange( function ( value ) { outlinePass.edgeGlow = Number( value );});
  folder.add( controls, 'edgeThickness', 1, 4 ).onChange( function ( value ) { outlinePass.edgeThickness = Number( value );});
  folder.add( controls, 'pulsePeriod', 0.0, 5 ).onChange( function ( value ) { outlinePass.pulsePeriod = Number( value );});

  var colors = {
    visibleEdgeColor: '#ffffff',
    hiddenEdgeColor: '#190a05'
  };

  folder.addColor( colors, 'visibleEdgeColor' ).onChange( function ( value ) {  outlinePass.visibleEdgeColor.set( value );});
  folder.addColor( colors, 'hiddenEdgeColor' ).onChange( function ( value ) { outlinePass.hiddenEdgeColor.set( value );});
}